﻿/******************************
软件：Tetris
别名: 俄罗斯方块
作者: su
完成日期: 2012-9-14
遗留问题：
- 画文字超出界面无法显示提示问题
- 局部刷新
- 速度问题因为定时器机制，导致10级之后没明显变化，没达到要求

修改记录：
2014.05.19 Ver: 1.2.2014.519
- 取消列对齐辅助线，仿照俄罗斯方块流行游戏功能，替换为指示当前方块下落位置，动态，更直观。

2013.11.07 Ver: 1.1.2013.1107
- 解决Tetris的位图对象内存泄露问题

2012.10.20 Ver: 1.0.2012.1020
- 检查游戏结束条件不变，(如果变为允许堆积到顶，需要生成新方块时，当前方块已经超出容器顶部，则游戏结束，则需要修改消行，最顶行需要填充为0)

2012.9.29 Ver: 1.0.2012.929
- 增加初速度选择，速度达到50后会反转
- 重新开始游戏会继承上次初速度，关闭程序才会再次出现速度选择界面
- 任何情况下都可以调整初速度，但调整完初速度后会重新开始游戏

2012.9.28 Ver: 1.0.2012.928
- 增加列对齐辅助线
- 使用实心矩形方块(空心，玩久了有些眼花)
- 解决当前下落方块最顶端贴边问题(未出现方块部分不画)
- 调整窗口创建style样式，不支持最大化，最小化，只支持关闭按钮

2012.9.20 Ver: 1.0.2012.920
- memcpy优化复制行
- 消行时一行行进行，有些复杂，继续优化

2012.9.19 Ver: 1.0.2012.919
- 内存越界问题已经解决
- 新增功能，按空格键直接落地
- 解决游戏结束，顶部绘新方块问题
- 新增功能，开始，暂停

2012.9.18 Ver: 1.0.2012.918
- 优化消行，消行只会出现在下落方块的4行里

2012.9.16 Ver: 1.0.2012.916
- 修正游戏首次生成方块。当前方块和下一方块随机数相同问题，
- 采用double buffer绘制游戏屏幕，去掉背景刷，解决重绘背景的区域闪烁问题。
- 显示下一方块。
- 加入消多行分数奖励。
- 增加速度等级，从1-50级。
- 按键Esc直接退出游戏。
- 开始程序时，显示在屏幕中间
- 解决方块刚出现，顶部无法旋转问题
- 解决游戏结束判断延迟问题
- 解决方块快堆满时，新方块碰撞计算失误问题

2012.9.15 Ver: 1.0.2012.915
- 修改随机数种子，避免随机数规律性出现。
- 修改旋转形状数组，为确保底部旋转不会导致方块上升一格，从而继续下降，旋转规则为保持底部水平(变形需要调整，到底后变形可以增高一行继续下落。)
- 优化代码，优化当前方块变量
******************************/

#include <windows.h>
#include "stdafx.h"
/* 方块细胞之间的参数 */
#define CELLS_WIDTH (30)
#define CELLS_DISTANCE (1)

/* 方块的种类 */
#define BLOCK_KINDS (7)
#define BLOCK_ROTATES (4)
#define BLOCK_WIDTH (4) /* 方块边长4 */
#define BLOCK_SIZE (16) /* 方块占用矩阵大小4x4 BLOCK_WIDTH x BLOCK_WIDTH */

/* 消多行分数奖励 */
static const int ScoreSets[BLOCK_WIDTH] = { 100, 400, 800, 1600 };

/* 容纳方块的空间 逻辑坐标系 从左到右width 从上到下high */
#define CONTAINER_WIDTH (10)
#define CONTAINER_HIGH (20)

/* 容器矩形坐标 */
RECT ContainerRect = { CELLS_DISTANCE, CELLS_DISTANCE,
CELLS_DISTANCE + CELLS_DISTANCE + CONTAINER_WIDTH * (CELLS_WIDTH + 1) + CELLS_DISTANCE + CELLS_DISTANCE,
CELLS_DISTANCE + CELLS_DISTANCE + CONTAINER_HIGH * (CELLS_WIDTH + 1) + CELLS_DISTANCE + CELLS_DISTANCE };

/* 下一个方块容器矩形坐标 */
RECT NextBlockRect = { CELLS_DISTANCE + CELLS_DISTANCE + CONTAINER_WIDTH * (CELLS_WIDTH + 1) + CELLS_DISTANCE + CELLS_DISTANCE + CELLS_DISTANCE + CELLS_DISTANCE,
CELLS_DISTANCE,
CELLS_DISTANCE + CELLS_DISTANCE + CONTAINER_WIDTH * (CELLS_WIDTH + 1) + CELLS_DISTANCE + CELLS_DISTANCE + CELLS_DISTANCE + CELLS_DISTANCE + CELLS_DISTANCE + BLOCK_WIDTH * (CELLS_WIDTH + 1) + CELLS_DISTANCE + CELLS_DISTANCE,
CELLS_DISTANCE + CELLS_DISTANCE + BLOCK_WIDTH * (CELLS_WIDTH + 1) + CELLS_DISTANCE + CELLS_DISTANCE };

/* 窗口大小 */
/* 窗口宽度 = 空隙 + 容器左边界 + 方块CONTAINER_WIDTH * (空隙 + CELLS_WIDTH) + 空隙 + 容器右边界 + 空隙 + 下一个容器左边界 + BLOCK_WIDTH * (空隙 + CELLS_WIDTH) + 空隙 + 下一个容器右边界 + 空隙 + 窗口两边占用部分 */
#define WINDOW_WIDTH (CELLS_DISTANCE + CELLS_DISTANCE + CONTAINER_WIDTH * (CELLS_WIDTH + 1) + CELLS_DISTANCE + CELLS_DISTANCE + CELLS_DISTANCE + CELLS_DISTANCE + BLOCK_WIDTH * (CELLS_WIDTH + 1) + CELLS_DISTANCE + CELLS_DISTANCE + CELLS_DISTANCE + 7) /* 7 为窗口边占用 */
/* 窗口高度 = 空隙 + 容器上边界 + 方块CONTAINER_HIGH * (空隙 + CELLS_WIDTH) + 空隙 + 容器下边界 + 空隙 + 窗口上下两边占用部分 */
#define WINDOW_HIGH (CELLS_DISTANCE + CELLS_DISTANCE + CONTAINER_HIGH * (CELLS_WIDTH + 1) + CELLS_DISTANCE + CELLS_DISTANCE + CELLS_DISTANCE + 33) /* 33 为标题栏高度 */

typedef struct Tetris
{
	unsigned int timerid;
	unsigned int GameState; /* 游戏状态，0，游戏运行； 1，游戏开始；2，游戏暂停 */
	unsigned int initalSpeed; /* 初速度 */
	unsigned int speed; /* 速度 */
	unsigned int score; /* 得分 */

						/* 下一个方块 */
	const unsigned char *next_block;
	unsigned int next_kind;
	unsigned int next_rotate;

	/* 当前方块 */
	const unsigned char *current_block;
	unsigned int kind;
	unsigned int rotate;
	int offset_left; /* 逻辑坐标 left, 正常范围 -min_left ~ CONTAINER_WIDTH - max_right */
	int offset_top; /* 逻辑坐标 top, 正常范围 -min_top ~ CONTAINER_HIGH - max_bottom */
	int offset_top_destination; /* 用来表明当前方块直线下落位置 */

	int min_left;
	int max_right;
	int min_top;
	int max_bottom;
} Tetris;

Tetris tetris = { 0 };

static unsigned char Container[CONTAINER_HIGH][CONTAINER_WIDTH] = { 0 };
/*   行列式
0 1 2 3 4 5 6 7 8 9 x CONTAINER_WIDTH
00□□□□□□□□□□
01□□□□□□□□□□
02□□□□□□□□□□
03□□□□□□□□□□
04□□□□□□□□□□
05□□□□□□□□□□
06□□□□□□□□□□
07□□□□□□□□□□
08□□□□□□□□□□
09□□□□□□□□□□
10□□□□□□□□□□
11□□□□□□□□□□
12□□□□□□□□□□
13□□□□□□□□□□
14□□□□□□□□□□
15□□□□□□□□□□
16□□□□□□□□□□
17□□□□□□□□□□
18□□□□□□□□□□
19□□□□□□□□□□
y
CONTAINER_HIGH
*/

static const unsigned char BlockSets[BLOCK_KINDS * BLOCK_ROTATES * BLOCK_SIZE] =
{
	/*
	所有方块的集合(I, J, L, O, S, T, Z)，每个方块依次顺时针旋转4次，每个4x4矩阵记录一个形状.
	为确保底部旋转不会导致方块上升一格，从而继续下降，旋转规则为保持底部水平
	▓
	▓    ▓   ▓
	▓    ▓   ▓    ▓▓     ▓▓   ▓▓▓   ▓▓
	▓  ▓▓   ▓▓  ▓▓   ▓▓       ▓       ▓▓
	*/
	/*
	I
	□■□□
	□■□□
	□■□□
	□■□□
	*/
	0, 1, 0, 0,
	0, 1, 0, 0,
	0, 1, 0, 0,
	0, 1, 0, 0,

	0, 0, 0, 0,
	0, 0, 0, 0,
	0, 0, 0, 0,
	1, 1, 1, 1,

	0, 1, 0, 0,
	0, 1, 0, 0,
	0, 1, 0, 0,
	0, 1, 0, 0,

	0, 0, 0, 0,
	0, 0, 0, 0,
	0, 0, 0, 0,
	1, 1, 1, 1,
	/*
	J
	□□□□
	□□■□
	□□■□
	□■■□
	*/
	0, 0, 0, 0,
	0, 0, 1, 0,
	0, 0, 1, 0,
	0, 1, 1, 0,

	0, 0, 0, 0,
	0, 0, 0, 0,
	0, 1, 0, 0,
	0, 1, 1, 1,

	0, 0, 0, 0,
	0, 1, 1, 0,
	0, 1, 0, 0,
	0, 1, 0, 0,

	0, 0, 0, 0,
	0, 0, 0, 0,
	1, 1, 1, 0,
	0, 0, 1, 0,
	/*
	L
	□□□□
	□■□□
	□■□□
	□■■□
	*/
	0, 0, 0, 0,
	0, 1, 0, 0,
	0, 1, 0, 0,
	0, 1, 1, 0,

	0, 0, 0, 0,
	0, 0, 0, 0,
	0, 1, 1, 1,
	0, 1, 0, 0,

	0, 0, 0, 0,
	0, 1, 1, 0,
	0, 0, 1, 0,
	0, 0, 1, 0,

	0, 0, 0, 0,
	0, 0, 0, 0,
	0, 0, 1, 0,
	1, 1, 1, 0,
	/*
	O
	□□□□
	□□□□
	□■■□
	□■■□
	*/
	0, 0, 0, 0,
	0, 0, 0, 0,
	0, 1, 1, 0,
	0, 1, 1, 0,

	0, 0, 0, 0,
	0, 0, 0, 0,
	0, 1, 1, 0,
	0, 1, 1, 0,

	0, 0, 0, 0,
	0, 0, 0, 0,
	0, 1, 1, 0,
	0, 1, 1, 0,

	0, 0, 0, 0,
	0, 0, 0, 0,
	0, 1, 1, 0,
	0, 1, 1, 0,
	/*
	S
	□□□□
	□□□□
	□■■□
	■■□□
	*/
	0, 0, 0, 0,
	0, 0, 0, 0,
	0, 1, 1, 0,
	1, 1, 0, 0,

	0, 0, 0, 0,
	0, 1, 0, 0,
	0, 1, 1, 0,
	0, 0, 1, 0,

	0, 0, 0, 0,
	0, 0, 0, 0,
	0, 1, 1, 0,
	1, 1, 0, 0,

	0, 0, 0, 0,
	0, 1, 0, 0,
	0, 1, 1, 0,
	0, 0, 1, 0,
	/*
	T
	□□□□
	□□□□
	■■■□
	□■□□
	*/
	0, 0, 0, 0,
	0, 0, 0, 0,
	1, 1, 1, 0,
	0, 1, 0, 0,

	0, 0, 0, 0,
	0, 1, 0, 0,
	1, 1, 0, 0,
	0, 1, 0, 0,

	0, 0, 0, 0,
	0, 0, 0, 0,
	0, 1, 0, 0,
	1, 1, 1, 0,

	0, 0, 0, 0,
	0, 1, 0, 0,
	0, 1, 1, 0,
	0, 1, 0, 0,
	/*
	Z
	□□□□
	□□□□
	■■□□
	□■■□
	*/
	0, 0, 0, 0,
	0, 0, 0, 0,
	1, 1, 0, 0,
	0, 1, 1, 0,

	0, 0, 0, 0,
	0, 0, 1, 0,
	0, 1, 1, 0,
	0, 1, 0, 0,

	0, 0, 0, 0,
	0, 0, 0, 0,
	1, 1, 0, 0,
	0, 1, 1, 0,

	0, 0, 0, 0,
	0, 0, 1, 0,
	0, 1, 1, 0,
	0, 1, 0, 0
};

/*------------------ 数据定义结束 ------------------*/

static void CheckGameOver(void)
{
	/*
	第一种判断方法：
	顶部有方块则游戏结束，
	第二种判断方法：
	方块堆积到顶部，每行都有方块，则游戏结束，
	反过来说，有一行没有方块，那就还没有结束
	*/
	int i = 0;
	int j = 0;
	int lineblockflags = 0; /* 0, 不存在方块， 1， 存在方块 */
	int gameoverflags = 1; /* 0, 不结束， 1， 结束 */

	for (i = 0; i < CONTAINER_HIGH && 1 == gameoverflags; ++i)
	{
		lineblockflags = 0;
		for (j = 0; j < CONTAINER_WIDTH && 0 == lineblockflags; ++j)
		{
			if (1 == Container[i][j])
			{
				lineblockflags = 1;
			}
		}
		if (0 == lineblockflags)
		{
			gameoverflags = 0;
		}
	}

	tetris.GameState = (0 == gameoverflags) ? 1 : 0;
}

static void CopyToContainer(void)
{
	int i = 0;
	int j = 0;

	for (i = tetris.min_top; i <= tetris.max_bottom; ++i)
	{
		for (j = tetris.min_left; j <= tetris.max_right; ++j)
		{
			if (1 == *(tetris.current_block + BLOCK_WIDTH * i + j)
				&& 0 <= tetris.offset_top + i && 0 <= tetris.offset_left + j)
			{   /* 只复制已经出现在游戏容器里面的方块 */
				Container[tetris.offset_top + i][tetris.offset_left + j] = 1;
			}
		}
	}
}

struct QUEUE_NODE_T
{
	int data;/* 字段内容 */
	struct QUEUE_NODE_T *next;/* 下一字段内容 */
};
typedef struct QUEUE_NODE_T QUEUE_NODE_T;

static unsigned int queue_push(QUEUE_NODE_T **head, int data)
{
	QUEUE_NODE_T *curnode = NULL;

	/* 创建尾节点 */
	if (NULL == *head)
	{
		/* 考虑字段链表头结点未出现的情况 */
		*head = (QUEUE_NODE_T *)malloc(sizeof(QUEUE_NODE_T));
		curnode = *head;
	}
	else
	{
		curnode = *head;
		/* 寻找尾节点 */
		while (NULL != curnode->next)
		{
			curnode = (QUEUE_NODE_T *)curnode->next;
		}
		/* 创建下一节点，并转到下一节点 */
		{
			curnode->next = (QUEUE_NODE_T *)malloc(sizeof(QUEUE_NODE_T));
			curnode = (QUEUE_NODE_T *)curnode->next;
		}
	}

	/* 尾节点赋值 */
	curnode->data = data;
	curnode->next = NULL;

	return 1;
}

static unsigned int queue_pop(QUEUE_NODE_T **head, int *data)
{
	QUEUE_NODE_T *temp = NULL;

	temp = (QUEUE_NODE_T *)(*head)->next;

	*data = (*head)->data;

	if (NULL != *head)
	{
		free(*head);
		*head = NULL;
	}

	*head = temp;

	return 1;
}

static void CaculateScore(void)
{
	QUEUE_NODE_T *head = NULL;
	unsigned int award = 0;
	int count = 0;
	int i = 0;
	int i_max = 0;
	int j = 0;

	/* 记录需要消去的行, 消行一定发生在放入积木块的4行 */
	/* 只考虑已经出现在游戏容器里面的方块 */
	i = (0 < tetris.offset_top + tetris.min_top) ? tetris.offset_top + tetris.min_top : 0;
	i_max = (0 < tetris.offset_top + tetris.max_bottom) ? tetris.offset_top + tetris.max_bottom : 0;
	for (; i <= i_max; ++i)
	{
		count = 0;

		for (j = 0; j < CONTAINER_WIDTH; ++j)
		{
			count += Container[i][j];
		}

		if (count == CONTAINER_WIDTH)
		{
			queue_push(&head, i);
			++award;
		}
	}

	if (0 != award)
	{
		/* 消行 */
		while (NULL != head)
		{
			int line = 0;
			queue_pop(&head, &line);

			for (i = line - 1; i >= 0; --i) /* row high */
			{
				memcpy(&Container[i + 1][0], &Container[i][0], sizeof(unsigned char) * CONTAINER_WIDTH);
			}

			/* 最顶层不可能出现方块消行(会导致游戏结束),所以保证了倒数第二行是空白的(从倒数第一行复制)，而倒数第一行空白也是合理的，不需调整 */
		}

		/* 得分 */
		tetris.score += ScoreSets[award - 1];

		/* 速度 */
		tetris.speed = tetris.score / 10000;
		tetris.speed += tetris.initalSpeed;
		tetris.speed %= 50;
	}

}

static void CaculateBlockBoundary(void)
{
	int i = 0;
	int j = 0;
	int isFounded = 0;

	/* CaculateMinLeft */
	isFounded = 0;
	for (j = 0; j < BLOCK_WIDTH && 0 == isFounded; ++j)
	{
		for (i = 0; i < BLOCK_WIDTH && 0 == isFounded; ++i)
		{
			if (1 == *(tetris.current_block + BLOCK_WIDTH * i + j))
			{
				tetris.min_left = j;
				isFounded = 1;
			}
		}
	}

	/* CaculateMaxRight */
	isFounded = 0;
	for (j = BLOCK_WIDTH - 1; j >= 0 && 0 == isFounded; --j)
	{
		for (i = 0; i < BLOCK_WIDTH && 0 == isFounded; ++i)
		{
			if (1 == *(tetris.current_block + BLOCK_WIDTH * i + j))
			{
				tetris.max_right = j;
				isFounded = 1;
			}
		}
	}

	/* CaculateMinTop */
	isFounded = 0;
	for (i = 0; i < BLOCK_WIDTH && 0 == isFounded; ++i)
	{
		for (j = 0; j < BLOCK_WIDTH && 0 == isFounded; ++j)
		{
			if (1 == *(tetris.current_block + BLOCK_WIDTH * i + j))
			{
				tetris.min_top = i;
				isFounded = 1;
			}
		}
	}

	/* CaculateMaxBottom */
	isFounded = 0;
	for (i = BLOCK_WIDTH - 1; i >= 0 && 0 == isFounded; --i)
	{
		for (j = 0; j < BLOCK_WIDTH && 0 == isFounded; ++j)
		{
			if (1 == *(tetris.current_block + BLOCK_WIDTH * i + j))
			{
				tetris.max_bottom = i;
				isFounded = 1;
			}
		}
	}
}


static void GenerateBlock(void)
{
	/* 检查游戏是否已经结束 */
	CheckGameOver();
	if (0 == tetris.GameState)
	{
		return;
	}

	if (NULL == tetris.current_block)
	{
		/* 第一次 */
		srand((unsigned int)time(0));
		tetris.kind = rand() % BLOCK_KINDS;
		tetris.rotate = rand() % BLOCK_ROTATES;
		tetris.current_block = BlockSets + tetris.kind * BLOCK_SIZE * BLOCK_ROTATES + tetris.rotate * BLOCK_SIZE;

		tetris.next_kind = rand() % BLOCK_KINDS;
		tetris.next_rotate = rand() % BLOCK_ROTATES;
	}
	else
	{
		tetris.kind = tetris.next_kind;
		tetris.rotate = tetris.next_rotate;
		tetris.current_block = tetris.next_block;

		srand((unsigned int)time(0));
		tetris.next_kind = rand() % BLOCK_KINDS;
		tetris.next_rotate = rand() % BLOCK_ROTATES;
	}
	tetris.next_block = BlockSets + tetris.next_kind * BLOCK_SIZE * BLOCK_ROTATES + tetris.next_rotate * BLOCK_SIZE;

	CaculateBlockBoundary();

	/* 新方块从中间落下，一行行下落 */
	tetris.offset_left = (CONTAINER_WIDTH - BLOCK_WIDTH) / 2 - 1 + 1; /* 因原始数据偏向左边，所以补偿1 */
	tetris.offset_top = -tetris.max_bottom;
}

static unsigned int DetectCollision(const unsigned char *block, int offset_left, int offset_top)
{
	/* return 2， 发生碰撞(新积木与容器内积木碰撞);1, 发生碰撞(超出左、右、下边框); 0 没有碰撞 */
	unsigned int state = 0;

	if (0 <= offset_left + tetris.min_left /* 左边界检测 */
		&& offset_left + tetris.max_right <= CONTAINER_WIDTH - 1 /* 右边界检测 */
																 /* 新方块可以允许上边界交叉 */
																 /* && 0 <= offset_top + tetris.min_top 上边界检测 */
		&& offset_top + tetris.max_bottom <= CONTAINER_HIGH - 1) /* 下边界检测 */
	{
		int i = 0;
		int j = 0;

		for (i = tetris.min_top; i <= tetris.max_bottom && 0 == state; ++i)
		{
			for (j = tetris.min_left; j <= tetris.max_right && 0 == state; ++j)
			{
				if (1 == *(block + BLOCK_WIDTH * i + j)
					&& 1 == Container[offset_top + i][offset_left + j])
				{
					/* 新方块可以允许上边界交叉，但不允许和下方容器内积木碰撞(重叠) */
					/* 只考虑方块与容器内积木碰撞，忽略超出上边界部分 */
					if (0 <= offset_top + tetris.min_top)
					{
						state = 2; /* 2， 发生碰撞(新积木与容器内积木碰撞) */
					}
				}
			}
		}
		/* state = 0; 如果一直没有碰撞，此时state值仍为0 */
	}
	else
	{
		state = 1; /* 1, 发生碰撞(超出左、右、下边框) */
	}

	return state;
}

static void StepLeft(void)
{
	--tetris.offset_left;
	if (DetectCollision(tetris.current_block, tetris.offset_left, tetris.offset_top))
	{
		++tetris.offset_left;
	}
}

static void StepRight(void)
{
	++tetris.offset_left;
	if (DetectCollision(tetris.current_block, tetris.offset_left, tetris.offset_top))
	{
		--tetris.offset_left;
	}
}

static int StepDown(void)
{
	/* return 0,触底 1 正常下移*/
	++tetris.offset_top;
	if (DetectCollision(tetris.current_block, tetris.offset_left, tetris.offset_top))
	{
		--tetris.offset_top;
		CopyToContainer();
		CaculateScore();
		GenerateBlock();

		return 0;
	}

	return 1;
}

static void UpdateCurrBlockDestinationPosition(void)
{
	tetris.offset_top_destination = tetris.offset_top;
	for (;;)
	{
		if (DetectCollision(tetris.current_block, tetris.offset_left, tetris.offset_top_destination))
		{
			--tetris.offset_top_destination;

			break;
		}

		++tetris.offset_top_destination;
	}
}

static void StepRotate(void)
{
	++tetris.rotate;
	tetris.rotate %= BLOCK_ROTATES;
	tetris.current_block = BlockSets + tetris.kind * BLOCK_SIZE * BLOCK_ROTATES + tetris.rotate * BLOCK_SIZE;

	/* 计算新边界 */
	CaculateBlockBoundary();

	/* 在边界处旋转需要调整offset_left,offset_top */
	/* 右边界 */
	if (tetris.offset_left + tetris.max_right > (CONTAINER_WIDTH - 1))
	{
		tetris.offset_left = (CONTAINER_WIDTH - 1) - tetris.max_right;
	}
	/* 左边界 */
	if (tetris.offset_left + tetris.min_left < 0)
	{
		tetris.offset_left = -tetris.min_left;
	}
	/* 下边界 */
	if (tetris.offset_top + tetris.max_bottom > CONTAINER_HIGH - 1)
	{
		tetris.offset_top = CONTAINER_HIGH - 1 - tetris.max_bottom;
	}

	if (DetectCollision(tetris.current_block, tetris.offset_left, tetris.offset_top))
	{
		--tetris.rotate;
		tetris.rotate %= BLOCK_ROTATES;
		tetris.current_block = BlockSets + tetris.kind * BLOCK_SIZE * BLOCK_ROTATES + tetris.rotate * BLOCK_SIZE;

		/* 恢复旋转前的边界 */
		CaculateBlockBoundary();
	}
}

static void OnDrawFrame(HDC hdc_mem)
{
	HPEN hPen = { 0 };
	HPEN OldPen = { 0 };

	/* 创建一个虚线画笔 */
	hPen = CreatePen(PS_DASH, 1, RGB(0xC0, 0xC0, 0xC0));
	/* 将虚线画笔分配给DC */
	OldPen = (HPEN)SelectObject(hdc_mem, hPen);

	/* 当前方块容器边界 */
	Rectangle(hdc_mem, ContainerRect.left, ContainerRect.top, ContainerRect.right, ContainerRect.bottom);

	/* 下一个方块容器边界 */
	Rectangle(hdc_mem, NextBlockRect.left, NextBlockRect.top, NextBlockRect.right, NextBlockRect.bottom);

#if 0
	/* 列对齐辅助线 */
	MoveToEx(hdc_mem, ContainerRect.left + (CONTAINER_WIDTH / 2) * (CELLS_WIDTH + 1) + CELLS_DISTANCE, ContainerRect.top + CELLS_DISTANCE, NULL);
	LineTo(hdc_mem, ContainerRect.left + (CONTAINER_WIDTH / 2) * (CELLS_WIDTH + 1) + CELLS_DISTANCE, ContainerRect.bottom - CELLS_DISTANCE);
#else
	/* 采用预显示最后下落位置来优化体验 */

#endif

	/* 恢复设置 */
	SelectObject(hdc_mem, OldPen);
	DeleteObject(hPen);

	return;
}

static void OnDrawSpeedSelect(HDC hdc_mem)
{
	TCHAR buffer[64] = { 0 };
	int size = 0;

	size = wsprintf(buffer, TEXT("初始速度增加 = ↑"));
	TextOut(hdc_mem, ContainerRect.left + (CONTAINER_WIDTH / 3) * CELLS_WIDTH, ContainerRect.top + (CONTAINER_HIGH / 2) * CELLS_WIDTH + 1 * CELLS_WIDTH, buffer, size);

	size = wsprintf(buffer, TEXT("初始速度减少 = ↓"));
	TextOut(hdc_mem, ContainerRect.left + (CONTAINER_WIDTH / 3) * CELLS_WIDTH, ContainerRect.top + (CONTAINER_HIGH / 2) * CELLS_WIDTH + 2 * CELLS_WIDTH, buffer, size);

	size = wsprintf(buffer, TEXT("设置结束开始游戏 = S"));
	TextOut(hdc_mem, ContainerRect.left + (CONTAINER_WIDTH / 3) * CELLS_WIDTH, ContainerRect.top + (CONTAINER_HIGH / 2) * CELLS_WIDTH + 3 * CELLS_WIDTH, buffer, size);

	size = wsprintf(buffer, TEXT("当前初始速度为: %d"), tetris.initalSpeed + 1);
	TextOut(hdc_mem, ContainerRect.left + (CONTAINER_WIDTH / 3) * CELLS_WIDTH, ContainerRect.top + (CONTAINER_HIGH / 2) * CELLS_WIDTH + 4 * CELLS_WIDTH, buffer, size);

	return;
}

static void OnDrawBlock(HDC hdc_mem)
{
	/*
	采用线性插值, 与需要的颜色做现场插值, fAlpha越小越接近RGB颜色修正基准值
	alpha * color1Src + (1-alpha) * color2Target;
	//想要变暗 color2取RGB(0,0,0)，alpha从0到1越小越黑
	//想要变亮 color2取RGB(255,255,255)alpha从0到1越小越白
	*/
	//float fAlpha = 0.15f;
	//BYTE byR = 0;
	//BYTE byG = 0;
	//BYTE byB = 0;
	HBRUSH hBrushPrompt = { 0 };

	HBRUSH hBrushBlue = { 0 };
	HBRUSH hOldBrush = { 0 };

	/* 创建一个实心刷子,系统用来绘制要填充图形的内部区域的位图 */
	hBrushBlue = CreateSolidBrush(RGB(0, 0, 255)); /* 蓝色 */

												   /* 创建一个浅色实心刷子,用来绘制要当前方块的最终下落提示位置 */
												   //byR = (BYTE)(fAlpha * 0 + (1 - fAlpha) * 0);
												   //byG = (BYTE)(fAlpha * 0 + (1 - fAlpha) * 0);
												   //byB = (BYTE)(fAlpha * 255 + (1 - fAlpha) * 0);
												   //hBrushPrompt = CreateSolidBrush(RGB(byR,byG,byB)); /* 蓝黑色 */
	hBrushPrompt = CreateSolidBrush(RGB(0x07, 0xFF, 0x00)); /* 绿色 */

															/* 将实心刷子分配给内存DC */
	hOldBrush = (HBRUSH)SelectObject(hdc_mem, hBrushPrompt);

	/********************************************
	DrawCurrentBlockDestinationPosition
	*********************************************/
	{
		int i = 0;
		int j = 0;

		for (i = 0; i < BLOCK_WIDTH; ++i)
		{
			for (j = 0; j < BLOCK_WIDTH; ++j)
			{
				/* 只画出现在容器内的方块 */
				if (1 == *(tetris.current_block + BLOCK_WIDTH * i + j)
					&& 0 <= tetris.offset_left + j && 0 <= tetris.offset_top_destination + i)
				{
					Rectangle(hdc_mem,
						ContainerRect.left + CELLS_DISTANCE + CELLS_DISTANCE + (tetris.offset_left + j) * (CELLS_WIDTH + 1),
						ContainerRect.top + CELLS_DISTANCE + CELLS_DISTANCE + (tetris.offset_top_destination + i) * (CELLS_WIDTH + 1),
						ContainerRect.left + CELLS_DISTANCE + CELLS_DISTANCE + (tetris.offset_left + j) * (CELLS_WIDTH + 1) + CELLS_WIDTH,
						ContainerRect.top + CELLS_DISTANCE + CELLS_DISTANCE + (tetris.offset_top_destination + i) * (CELLS_WIDTH + 1) + CELLS_WIDTH);
				}
			}
		}
	}

	/* 将实心刷子分配给内存DC */
	hOldBrush = (HBRUSH)SelectObject(hdc_mem, hBrushBlue);
	DeleteObject(hBrushPrompt);

	/********************************************
	DrawCurrentBlock
	*********************************************/
	{
		int i = 0;
		int j = 0;

		for (i = 0; i < BLOCK_WIDTH; ++i)
		{
			for (j = 0; j < BLOCK_WIDTH; ++j)
			{
				/* 只画出现在容器内的方块 */
				if (1 == *(tetris.current_block + BLOCK_WIDTH * i + j)
					&& 0 <= tetris.offset_left + j && 0 <= tetris.offset_top + i)
				{
					Rectangle(hdc_mem,
						ContainerRect.left + CELLS_DISTANCE + CELLS_DISTANCE + (tetris.offset_left + j) * (CELLS_WIDTH + 1),
						ContainerRect.top + CELLS_DISTANCE + CELLS_DISTANCE + (tetris.offset_top + i) * (CELLS_WIDTH + 1),
						ContainerRect.left + CELLS_DISTANCE + CELLS_DISTANCE + (tetris.offset_left + j) * (CELLS_WIDTH + 1) + CELLS_WIDTH,
						ContainerRect.top + CELLS_DISTANCE + CELLS_DISTANCE + (tetris.offset_top + i) * (CELLS_WIDTH + 1) + CELLS_WIDTH);
				}
			}
		}
	}

	/*****************************************
	DrawContainer
	******************************************/
	{
		int i = 0;
		int j = 0;
		for (i = 0; i < CONTAINER_HIGH; ++i)
		{
			for (j = 0; j < CONTAINER_WIDTH; ++j)
			{
				if (1 == Container[i][j])
				{
					Rectangle(hdc_mem,
						ContainerRect.left + CELLS_DISTANCE + CELLS_DISTANCE + j * (CELLS_WIDTH + 1),
						ContainerRect.top + CELLS_DISTANCE + CELLS_DISTANCE + i * (CELLS_WIDTH + 1),
						ContainerRect.left + CELLS_DISTANCE + CELLS_DISTANCE + j * (CELLS_WIDTH + 1) + CELLS_WIDTH,
						ContainerRect.top + CELLS_DISTANCE + CELLS_DISTANCE + i * (CELLS_WIDTH + 1) + CELLS_WIDTH);
				}
			}
		}
	}

	/********************************************
	DrawNextBlock
	*********************************************/
	{
		int i = 0;
		int j = 0;

		for (i = 0; i < BLOCK_WIDTH; ++i)
		{
			for (j = 0; j < BLOCK_WIDTH; ++j)
			{
				if (1 == *(tetris.next_block + BLOCK_WIDTH * i + j))
				{
					Rectangle(hdc_mem,
						NextBlockRect.left + CELLS_DISTANCE + CELLS_DISTANCE + j * (CELLS_WIDTH + 1),
						NextBlockRect.top + CELLS_DISTANCE + CELLS_DISTANCE + i * (CELLS_WIDTH + 1),
						NextBlockRect.left + CELLS_DISTANCE + CELLS_DISTANCE + j * (CELLS_WIDTH + 1) + CELLS_WIDTH,
						NextBlockRect.top + CELLS_DISTANCE + CELLS_DISTANCE + i * (CELLS_WIDTH + 1) + CELLS_WIDTH);
				}
			}
		}
	}

	/* 恢复设置 */
	SelectObject(hdc_mem, hOldBrush);
	DeleteObject(hBrushBlue);
	return;
}

static void OnDrawResult(HDC hdc_mem)
{
	TCHAR buffer[64] = { 0 };
	int size = 0;

	size = wsprintf(buffer, TEXT("%d"), tetris.score);
	TextOut(hdc_mem, NextBlockRect.left + CELLS_DISTANCE, NextBlockRect.bottom + 1 * CELLS_WIDTH, TEXT("Score:"), 6);
	TextOut(hdc_mem, NextBlockRect.left + CELLS_DISTANCE, NextBlockRect.bottom + 2 * CELLS_WIDTH, buffer, size);

	size = wsprintf(buffer, TEXT("%d"), tetris.speed + 1);
	TextOut(hdc_mem, NextBlockRect.left + CELLS_DISTANCE, NextBlockRect.bottom + 3 * CELLS_WIDTH, TEXT("Speed:"), 6);
	TextOut(hdc_mem, NextBlockRect.left + CELLS_DISTANCE, NextBlockRect.bottom + 4 * CELLS_WIDTH, buffer, size);

	switch (tetris.GameState)
	{
	case 0:
	{
		TextOut(hdc_mem, NextBlockRect.left + CELLS_DISTANCE, NextBlockRect.bottom + 5 * CELLS_WIDTH, TEXT("Game Over!!!"), 12);
	}
	break;
	case 1:
	{
	}
	break;
	case 2:
	{
		TextOut(hdc_mem, NextBlockRect.left + CELLS_DISTANCE, NextBlockRect.bottom + 5 * CELLS_WIDTH, TEXT("Game Pause"), 10);
	}
	break;
	default:
	{
	}
	break;
	}

	size = wsprintf(buffer, TEXT("开始= S"));
	TextOut(hdc_mem, NextBlockRect.left + CELLS_DISTANCE, NextBlockRect.bottom + 6 * CELLS_WIDTH, buffer, size);

	size = wsprintf(buffer, TEXT("暂停= P"));
	TextOut(hdc_mem, NextBlockRect.left + CELLS_DISTANCE, NextBlockRect.bottom + 7 * CELLS_WIDTH, buffer, size);

	size = wsprintf(buffer, TEXT("退出= Esc"));
	TextOut(hdc_mem, NextBlockRect.left + CELLS_DISTANCE, NextBlockRect.bottom + 8 * CELLS_WIDTH, buffer, size);

	size = wsprintf(buffer, TEXT("旋转= ↑"));
	TextOut(hdc_mem, NextBlockRect.left + CELLS_DISTANCE, NextBlockRect.bottom + 9 * CELLS_WIDTH, buffer, size);

	size = wsprintf(buffer, TEXT("左移= ←"));
	TextOut(hdc_mem, NextBlockRect.left + CELLS_DISTANCE, NextBlockRect.bottom + 10 * CELLS_WIDTH, buffer, size);

	size = wsprintf(buffer, TEXT("右移= →"));
	TextOut(hdc_mem, NextBlockRect.left + CELLS_DISTANCE, NextBlockRect.bottom + 11 * CELLS_WIDTH, buffer, size);

	size = wsprintf(buffer, TEXT("下移= ↓"));
	TextOut(hdc_mem, NextBlockRect.left + CELLS_DISTANCE, NextBlockRect.bottom + 12 * CELLS_WIDTH, buffer, size);

	size = wsprintf(buffer, TEXT("落地= Space"));
	TextOut(hdc_mem, NextBlockRect.left + CELLS_DISTANCE, NextBlockRect.bottom + 13 * CELLS_WIDTH, buffer, size);

	size = wsprintf(buffer, TEXT("初速度= C"));
	TextOut(hdc_mem, NextBlockRect.left + CELLS_DISTANCE, NextBlockRect.bottom + 14 * CELLS_WIDTH, buffer, size);

	return;
}

static void OnPaint(HDC hdc)
{
	HDC hdc_mem = { 0 };
	HBITMAP hBitmap = { 0 };
	HBITMAP hOldBitmap = { 0 };

	/* 创建内存DC */
	hdc_mem = CreateCompatibleDC(hdc);
	/* 创建一个bmp内存空间 */
	hBitmap = CreateCompatibleBitmap(hdc, WINDOW_WIDTH, WINDOW_HIGH);
	/* 将bmp内存空间分配给内存DC */
	hOldBitmap = (HBITMAP)SelectObject(hdc_mem, hBitmap);
	/* 先用背景色将位图清除干净，这里用的是白色作为背景 */
	PatBlt(hdc_mem, 0, 0, WINDOW_WIDTH, WINDOW_HIGH, WHITENESS);

	OnDrawFrame(hdc_mem);

	if (3 == tetris.GameState)
	{
		/* 速度选择 */
		OnDrawSpeedSelect(hdc_mem);
	}
	else
	{
		/* 正常游戏 */
		UpdateCurrBlockDestinationPosition();
		OnDrawBlock(hdc_mem);
	}

	OnDrawResult(hdc_mem);

	/* 将内存DC的内容复制到屏幕显示DC中,完成显示 */
	BitBlt(hdc, 0, 0, WINDOW_WIDTH, WINDOW_HIGH, hdc_mem, 0, 0, SRCCOPY);

	SelectObject(hdc_mem, hOldBitmap);
	DeleteObject(hBitmap);
	DeleteDC(hdc_mem);
}

static LRESULT CALLBACK WindowMessagesProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	HDC hdc = { 0 };
	PAINTSTRUCT ps = { 0 };
	LRESULT rs = 0;

	switch (message)
	{
	case WM_CREATE:
	{
		tetris.GameState = 3;
		tetris.initalSpeed = 0;
		SetTimer(hWnd, tetris.timerid, 20, (TIMERPROC)NULL);
	}
	break;
	case WM_TIMER:
	{
		if (3 != tetris.GameState)
		{
			static unsigned int timercounter = 0;
			static unsigned int interval = 0;
			++timercounter;
			interval = 1000 / (tetris.speed + 1);
			if ((timercounter * 20) >= interval)
			{
				if (1 == tetris.GameState)
				{
					StepDown();
				}

				timercounter = 0;
			}
			InvalidateRect(hWnd, NULL, 0);
		}
	}
	break;
	case WM_KEYDOWN:
	{
		switch (wParam)
		{
		case VK_UP:
		{
			if (1 == tetris.GameState)
			{
				StepRotate();
			}
			if (3 == tetris.GameState)
			{
				++tetris.initalSpeed;
				tetris.initalSpeed = tetris.initalSpeed > 49 ? 0 : tetris.initalSpeed;
			}
		}
		break;
		case VK_LEFT:
		{
			if (1 == tetris.GameState)
			{
				StepLeft();
			}
		}
		break;
		case VK_RIGHT:
		{
			if (1 == tetris.GameState)
			{
				StepRight();
			}
		}
		break;
		case VK_DOWN:
		{
			if (1 == tetris.GameState)
			{
				StepDown();
			}
			if (3 == tetris.GameState)
			{
				--tetris.initalSpeed;
				/* 无符号整数范围0 - ~0，0往下会转为最大数 */
				tetris.initalSpeed = tetris.initalSpeed > 49 ? 49 : tetris.initalSpeed;
			}
		}
		break;
		case VK_SPACE:
		{
			if (1 == tetris.GameState)
			{
				/* 按Space键落地 */
				while (1 == StepDown())
				{
				}
			}
		}
		break;
		case 'C':
		{
			/* 调整初速度，之后会直接开始新游戏 */
			if (3 != tetris.GameState)
			{
				tetris.GameState = 3;
			}
		}
		break;
		case 'P':
		{
			if (1 == tetris.GameState)
			{
				/* 按P键暂停游戏 */
				tetris.GameState = 2;
			}
		}
		break;
		case 'S':
		{
			/* 按S键离开暂停状态 */
			if (2 == tetris.GameState)
			{
				tetris.GameState = 1;
			}
			/* 按S键重新开始游戏 */
			if ((0 == tetris.GameState) || (3 == tetris.GameState))
			{
				/* 保留初始化速度 */
				unsigned int temp = tetris.initalSpeed;
				/* 初始化游戏 */
				memset((void *)Container, 0, sizeof(unsigned char) * CONTAINER_HIGH * CONTAINER_WIDTH);
				memset((void *)&tetris, 0, sizeof(struct Tetris));
				tetris.GameState = 1;
				tetris.score = 0;
				tetris.initalSpeed = temp;
				tetris.speed = 0 + tetris.initalSpeed;
				GenerateBlock();
			}
		}
		break;
		case VK_ESCAPE:
		{
			/* 按Esc键退出游戏 */
			PostMessage(hWnd, WM_DESTROY, (WPARAM)NULL, (LPARAM)NULL);
		}
		break;
		default:
		{
			rs = DefWindowProc(hWnd, message, wParam, lParam); /* default windows proc */
		}
		break;
		}
		InvalidateRect(hWnd, NULL, 0);
	}
	break;
	case WM_PAINT:
	{
		hdc = BeginPaint(hWnd, &ps);
		OnPaint(hdc);
		EndPaint(hWnd, &ps);
	}
	break;
	case WM_ERASEBKGND:
	{
		/* 禁止系统刷新背景 */
	}
	break;
	case WM_DESTROY:
	{
		KillTimer(hWnd, tetris.timerid);
		PostQuitMessage(0);
	}
	break;
	default:
	{
		rs = DefWindowProc(hWnd, message, wParam, lParam); /* default windows proc */
	}
	break;
	}

	return rs;
}

INT WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevlnstance, PSTR lpCmdLine, INT iCmdShow)
{
	HWND hWnd = { 0 };
	MSG msg = { 0 };
	HWND hWndDestop = { 0 };
	RECT DestopRect = { 0 };
	WNDCLASS wndClass = { 0 };

	wndClass.style = CS_HREDRAW | CS_VREDRAW;
	wndClass.lpfnWndProc = WindowMessagesProc;
	wndClass.cbClsExtra = 0;
	wndClass.cbWndExtra = 0;
	wndClass.hInstance = hInstance;
	wndClass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
	wndClass.hCursor = LoadCursor(NULL, IDC_ARROW);
	wndClass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); /* 背景刷 */
	wndClass.lpszMenuName = NULL;
	wndClass.lpszClassName = TEXT("Tetris");

	RegisterClass(&wndClass);

	hWndDestop = GetDesktopWindow();
	GetClientRect(hWndDestop, &DestopRect);

	hWnd = CreateWindow(
		TEXT("Tetris"),        /* window class name */
		TEXT("Tetris"),        /* window caption */
		WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU, //WS_OVERLAPPEDWINDOW,   /* window style */
		(DestopRect.right - WINDOW_WIDTH) / 2,         /* initial x position default CW_USEDEFAULT */
		(DestopRect.bottom - WINDOW_HIGH) / 2,         /* initial y position default CW_USEDEFAULT */
		WINDOW_WIDTH,                   /* initial x size */
		WINDOW_HIGH,                   /* initial y size */
		NULL,                  /* parent window handle */
		NULL,                  /* window menu handle */
		hInstance,             /* program instance handle */
		NULL);                 /* creation parameters */

	ShowWindow(hWnd, iCmdShow);
	UpdateWindow(hWnd);

	while (GetMessage(&msg, NULL, 0, 0))
	{
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}

	return msg.wParam;
}

